/**
 * 
 */
package com.workable.assignment.client;

import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.concurrent.Future;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.sun.jersey.api.client.AsyncWebResource;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientHandlerException;
import com.sun.jersey.api.client.ClientRequest;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.filter.ClientFilter;
import com.workable.assignment.domain.rottentomatoes.RTMovie;
import com.workable.assignment.domain.rottentomatoes.RTMovieReviews;
import com.workable.assignment.domain.themoviedb.TMDBMovie;
import com.workable.assignment.domain.themoviedb.TMDBMovieDetails;
import com.workable.assignment.domain.themoviedb.TMDBMovieReviews;

/**
 * @author npapadopoulos
 * @date Oct 23, 2014
 * 
 */
public abstract class AbstractMovieRamaClient {

    private static final Logger LOG = (Logger) LoggerFactory.getLogger(AbstractMovieRamaClient.class);

    private static final Client CLIENT = Client.create();

    private ObjectMapper mapper;

    /**
     * Async call functionality
     * 
     * @param url
     * @return Future<ClientResponse>
     */
    protected Future<ClientResponse> callAsync(String url) {
        final AsyncWebResource resource = CLIENT.asyncResource(url);
        resource.addFilter(getFilter());

        if (LOG.isDebugEnabled()) {
            LOG.debug(url);
        }
        return resource.header("Content-Type", "application/json").get(ClientResponse.class);
    }

    /**
     * Sync call functionality
     * 
     * @param url
     * @return ClientResponse
     */
    protected ClientResponse call(String url) {
        final WebResource resource = CLIENT.resource(url);
        resource.addFilter(getFilter());

        if (LOG.isDebugEnabled()) {
            LOG.debug(url);
        }
        return resource.header("Content-Type", "application/json").get(ClientResponse.class);
    }

    protected ObjectMapper getObjectMapper() {

        if (mapper == null) {
            return new ObjectMapper();
        }

        return mapper;
    }

    /**
     * Recovery mechanism, when the remote host is unavailable 
     * 
     * @return ClientFilter
     */
    private ClientFilter getFilter() {
        return new ClientFilter() {
            private final int maxRetries = 2;

            public ClientResponse handle(ClientRequest cr) throws ClientHandlerException {
                int i = 0;
                while (i < maxRetries) {
                    i++;
                    try {
                        return getNext().handle(cr);
                    } catch (ClientHandlerException e) {
                        LOG.error(e.getMessage(), e);
                    }
                }
                throw new ClientHandlerException("Connection retries limit exceeded.");
            };
        };
    }

    abstract public List<RTMovie> getRTMoviesInTheater();

    abstract public List<RTMovie> getSearchedRTMovies(String query) throws UnsupportedEncodingException;

    abstract public RTMovieReviews getRTMovieReviews(long id);

    abstract public List<TMDBMovie> getTMDBMoviesInTheater();

    abstract public List<TMDBMovie> getSearchedTMDBMovies(String query) throws UnsupportedEncodingException;

    abstract public TMDBMovieDetails getTMDBMovieDetails(long id);

    abstract public TMDBMovieReviews getTMDBMovieReviews(long id);

}
